#!/usr/bin/python

import sys, getopt, time, os
import pexpect, re
import telnetlib
import atexit
import __main__

## do not add code here.
#BEGIN_VERSION_GENERATION
RELEASE_VERSION = "3.1.5"
BUILD_DATE = "(built Thu Jul 18 08:52:45 CST 2013)"
REDHAT_COPYRIGHT = "Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved."
#END_VERSION_GENERATION

LOG_MODE_VERBOSE = 100
LOG_MODE_QUIET = 0

EC_GENERIC_ERROR = 1
EC_BAD_ARGS = 2
EC_LOGIN_DENIED = 3
EC_CONNECTION_LOST = 4
EC_TIMED_OUT = 5
EC_WAITING_ON = 6
EC_WAITING_OFF = 7
EC_STATUS = 8
EC_STATUS_HMC = 9

TELNET_PATH = "/usr/bin/telnet"
SSH_PATH = "/usr/bin/ssh"
SSL_PATH = "/usr/sbin/fence_nss_wrapper"

all_opt = {
    "help": {
        "getopt": "h",
        "longopt": "help",
        "help": "-h, --help                     Display this help and exit",
        "required": "0",
        "shortdesc": "Display help and exit",
        "order": 54},
    "version": {
        "getopt": "V",
        "longopt": "version",
        "help": "-V, --version                  Output version information and exit",
        "required": "0",
        "shortdesc": "Display version information and exit",
        "order": 53},
    "quiet": {
        "getopt": "q",
        "help": "",
        "order": 50},
    "verbose": {
        "getopt": "v",
        "longopt": "verbose",
        "help": "-v, --verbose                  Verbose mode",
        "required": "0",
        "shortdesc": "Verbose mode",
        "order": 51},
    "debug": {
        "getopt": "D:",
        "longopt": "debug-file",
        "help": "-D, --debug-file=<debugfile>   Debugging to output file",
        "required": "0",
        "shortdesc": "Write debug information to given file",
        "order": 52},
    "delay": {
        "getopt": "f:",
        "longopt": "delay",
        "help": "--delay <seconds>              Wait X seconds before fencing is started",
        "required": "0",
        "shortdesc": "Wait X seconds before fencing is started",
        "default": "0",
        "order": 200},
    "agent": {
        "getopt": "",
        "help": "",
        "order": 1},
    "web": {
        "getopt": "",
        "help": "",
        "order": 1},
    "action": {
        "getopt": "o:",
        "longopt": "action",
        "help": "-o, --action=<action>          Action: status, reboot (default), off or on",
        "required": "1",
        "shortdesc": "Fencing Action",
        "default": "reboot",
        "order": 1},
    "io_fencing": {
        "getopt": "o:",
        "longopt": "action",
        "help": "-o, --action=<action>          Action: status, enable or disable",
        "required": "1",
        "shortdesc": "Fencing Action",
        "default": "disable",
        "order": 1},
    "ipaddr": {
        "getopt": "a:",
        "longopt": "ip",
        "help": "-a, --ip=<ip>                  IP address or hostname of fencing device",
        "required": "1",
        "shortdesc": "IP Address or Hostname",
        "order": 1},
    "ipport": {
        "getopt": "u:",
        "longopt": "ipport",
        "help": "-u, --ipport=<port>            TCP port to use",
        "required": "0",
        "shortdesc": "TCP port to use for connection with device",
        "order": 1},
    "login": {
        "getopt": "l:",
        "longopt": "username",
        "help": "-l, --username=<name>          Login name",
        "required": "?",
        "shortdesc": "Login Name",
        "order": 1},
    "no_login": {
        "getopt": "",
        "help": "",
        "order": 1},
    "no_password": {
        "getopt": "",
        "help": "",
        "order": 1},
    "passwd": {
        "getopt": "p:",
        "longopt": "password",
        "help": "-p, --password=<password>      Login password or passphrase",
        "required": "0",
        "shortdesc": "Login password or passphrase",
        "order": 1},
    "passwd_script": {
        "getopt": "S:",
        "longopt": "password-script=",
        "help": "-S, --password-script=<script> Script to run to retrieve password",
        "required": "0",
        "shortdesc": "Script to retrieve password",
        "order": 1},
    "identity_file": {
        "getopt": "k:",
        "longopt": "identity-file",
        "help": "-k, --identity-file=<filename> Identity file (private key) for ssh ",
        "required": "0",
        "shortdesc": "Identity file for ssh",
        "order": 1},
    "module_name": {
        "getopt": "m:",
        "longopt": "module-name",
        "help": "-m, --module-name=<module>     DRAC/MC module name",
        "required": "0",
        "shortdesc": "DRAC/MC module name",
        "order": 1},
    "drac_version": {
        "getopt": "d:",
        "longopt": "drac-version",
        "help": "-d, --drac-version=<version>   Force DRAC version to use",
        "required": "0",
        "shortdesc": "Force DRAC version to use",
        "order": 1},
    "hmc_version": {
        "getopt": "H:",
        "longopt": "hmc-version",
        "help": "-H, --hmc-version=<version>    Force HMC version to use: 3, 4 (default)",
        "required": "0",
        "shortdesc": "Force HMC version to use (3 or 4)",
        "default": "4",
        "order": 1},
    "ribcl": {
        "getopt": "r:",
        "longopt": "ribcl-version",
        "help": "-r, --ribcl-version=<version>  Force ribcl version to use",
        "required": "0",
        "shortdesc": "Force ribcl version to use",
        "order": 1},
    "login_eol_lf": {
        "getopt": "",
        "help": "",
        "order": 1
    },
    "cmd_prompt": {
        "getopt": "c:",
        "longopt": "command-prompt",
        "help": "-c, --command-prompt=<prompt>  Force command prompt",
        "shortdesc": "Force command prompt",
        "required": "0",
        "order": 1},
    "secure": {
        "getopt": "x",
        "longopt": "ssh",
        "help": "-x, --ssh                      Use ssh connection",
        "shortdesc": "SSH connection",
        "required": "0",
        "order": 1},
    "ssl": {
        "getopt": "z",
        "longopt": "ssl",
        "help": "-z, --ssl                      Use ssl connection",
        "required": "0",
        "shortdesc": "SSL connection",
        "order": 1},
    "port": {
        "getopt": "n:",
        "longopt": "plug",
        "help": "-n, --plug=<id>                Physical plug number on device or\n" +
                "                                        name of virtual machine",
        "required": "1",
        "shortdesc": "Physical plug number or name of virtual machine",
        "order": 1},
    "switch": {
        "getopt": "s:",
        "longopt": "switch",
        "help": "-s, --switch=<id>              Physical switch number on device",
        "required": "0",
        "shortdesc": "Physical switch number on device",
        "order": 1},
    "suborg": {
        "getopt": "s:",
        "longopt": "suborg",
        "help": "--suborg=<path>                Additional path needed to access suborganization",
        "required": "0",
        "shortdesc": "Additional path needed to access suborganization",
        "default": "",
        "order": 1},
    "partition": {
        "getopt": "n:",
        "help": "-n <id>                        Name of the partition",
        "required": "0",
        "shortdesc": "Partition name",
        "order": 1},
    "managed": {
        "getopt": "s:",
        "help": "-s <id>                        Name of the managed system",
        "required": "0",
        "shortdesc": "Managed system name",
        "order": 1},
    "test": {
        "getopt": "T",
        "help": "",
        "order": 1,
        "obsolete": "use -o status instead"},
    "exec": {
        "getopt": "e:",
        "longopt": "exec",
        "help": "-e, --exec=<command>           Command to execute",
        "required": "0",
        "shortdesc": "Command to execute",
        "order": 1},
    "vmware_type": {
        "getopt": "d:",
        "longopt": "vmware_type",
        "help": "-d, --vmware_type=<type>       Type of VMware to connect",
        "required": "0",
        "shortdesc": "Type of VMware to connect",
        "order": 1},
    "vmware_datacenter": {
        "getopt": "s:",
        "longopt": "vmware-datacenter",
        "help": "-s, --vmware-datacenter=<dc>   VMWare datacenter filter",
        "required": "0",
        "shortdesc": "Show only machines in specified datacenter",
        "order": 2},
    "snmp_version": {
        "getopt": "d:",
        "longopt": "snmp-version",
        "help": "-d, --snmp-version=<ver>       Specifies SNMP version to use",
        "required": "0",
        "shortdesc": "Specifies SNMP version to use (1,2c,3)",
        "order": 1},
    "community": {
        "getopt": "c:",
        "longopt": "community",
        "help": "-c, --community=<community>    Set the community string",
        "required": "0",
        "shortdesc": "Set the community string",
        "order": 1},
    "snmp_auth_prot": {
        "getopt": "b:",
        "longopt": "snmp-auth-prot",
        "help": "-b, --snmp-auth-prot=<prot>    Set authentication protocol (MD5|SHA)",
        "required": "0",
        "shortdesc": "Set authentication protocol (MD5|SHA)",
        "order": 1},
    "snmp_sec_level": {
        "getopt": "E:",
        "longopt": "snmp-sec-level",
        "help": "-E, --snmp-sec-level=<level>   Set security level\n" +
                "                                  (noAuthNoPriv|authNoPriv|authPriv)",
        "required": "0",
        "shortdesc": "Set security level (noAuthNoPriv|authNoPriv|authPriv)",
        "order": 1},
    "snmp_priv_prot": {
        "getopt": "B:",
        "longopt": "snmp-priv-prot",
        "help": "-B, --snmp-priv-prot=<prot>    Set privacy protocol (DES|AES)",
        "required": "0",
        "shortdesc": "Set privacy protocol (DES|AES)",
        "order": 1},
    "snmp_priv_passwd": {
        "getopt": "P:",
        "longopt": "snmp-priv-passwd",
        "help": "-P, --snmp-priv-passwd=<pass>  Set privacy protocol password",
        "required": "0",
        "shortdesc": "Set privacy protocol password",
        "order": 1},
    "snmp_priv_passwd_script": {
        "getopt": "R:",
        "longopt": "snmp-priv-passwd-script",
        "help": "-R, --snmp-priv-passwd-script  Script to run to retrieve privacy password",
        "required": "0",
        "shortdesc": "Script to run to retrieve privacy password",
        "order": 1},
    "inet4_only": {
        "getopt": "4",
        "longopt": "inet4-only",
        "help": "-4, --inet4-only               Forces agent to use IPv4 addresses only",
        "required": "0",
        "shortdesc": "Forces agent to use IPv4 addresses only",
        "order": 1},
    "inet6_only": {
        "getopt": "6",
        "longopt": "inet6-only",
        "help": "-6, --inet6-only               Forces agent to use IPv6 addresses only",
        "required": "0",
        "shortdesc": "Forces agent to use IPv6 addresses only",
        "order": 1},
    "udpport": {
        "getopt": "u:",
        "longopt": "udpport",
        "help": "-u, --udpport                  UDP/TCP port to use",
        "required": "0",
        "shortdesc": "UDP/TCP port to use for connection with device",
        "order": 1},
    "separator": {
        "getopt": "C:",
        "longopt": "separator",
        "help": "-C, --separator=<char>         Separator for CSV created by 'list' operation",
        "default": ",",
        "required": "0",
        "shortdesc": "Separator for CSV created by operation list",
        "order": 100},
    "login_timeout": {
        "getopt": "y:",
        "longopt": "login-timeout",
        "help": "--login-timeout <seconds>      Wait X seconds for cmd prompt after login",
        "default": "5",
        "required": "0",
        "shortdesc": "Wait X seconds for cmd prompt after login",
        "order": 200},
    "shell_timeout": {
        "getopt": "Y:",
        "longopt": "shell-timeout",
        "help": "--shell-timeout <seconds>      Wait X seconds for cmd prompt after issuing command",
        "default": "3",
        "required": "0",
        "shortdesc": "Wait X seconds for cmd prompt after issuing command",
        "order": 200},
    "power_timeout": {
        "getopt": "g:",
        "longopt": "power-timeout",
        "help": "--power-timeout <seconds>      Test X seconds for status change after ON/OFF",
        "default": "20",
        "required": "0",
        "shortdesc": "Test X seconds for status change after ON/OFF",
        "order": 200},
    "power_wait": {
        "getopt": "G:",
        "longopt": "power-wait",
        "help": "--power-wait <seconds>         Wait X seconds after issuing ON/OFF",
        "default": "0",
        "required": "0",
        "shortdesc": "Wait X seconds after issuing ON/OFF",
        "order": 200},
    "missing_as_off": {
        "getopt": "M",
        "longopt": "missing-as-off",
        "help": "--missing-as-off               Missing port returns OFF instead of failure",
        "required": "0",
        "shortdesc": "Missing port returns OFF instead of failure",
        "order": 200},
    "retry_on": {
        "getopt": "F:",
        "longopt": "retry-on",
        "help": "--retry-on <attempts>          Count of attempts to retry power on",
        "default": "1",
        "required": "0",
        "shortdesc": "Count of attempts to retry power on",
        "order": 201},
    "session_url": {
        "getopt": "s:",
        "longopt": "session-url",
        "help": "-s, --session-url              URL to connect to XenServer on.",
        "required": "1",
        "shortdesc": "The URL of the XenServer host.",
        "order": 1},
    "uuid": {
        "getopt": "U:",
        "longopt": "uuid",
        "help": "-U, --uuid                     UUID of the VM to fence.",
        "required": "0",
        "shortdesc": "The UUID of the virtual machine to fence.",
        "order": 1}
}

common_opt = ["retry_on", "delay"]


class fspawn(pexpect.spawn):
    def log_expect(self, options, pattern, timeout):
        result = self.expect(pattern, timeout)
        if options["log"] >= LOG_MODE_VERBOSE:
            options["debug_fh"].write(self.before + self.after)
        return result


def atexit_handler():
    try:
        sys.stdout.close()
        os.close(1)
    except IOError:
        sys.stderr.write("%s failed to close standard output\n" % (sys.argv[0]))
        sys.exit(EC_GENERIC_ERROR)


def version(command, release, build_date, copyright_notice):
    print command, " ", release, " ", build_date
    if len(copyright_notice) > 0:
        print copyright_notice


def fail_usage(message=""):
    if len(message) > 0:
        sys.stderr.write(message + "\n")
    sys.stderr.write("Please use '-h' for usage\n")
    sys.exit(EC_GENERIC_ERROR)


def fail(error_code):
    message = {
                  EC_LOGIN_DENIED: "Unable to connect/login to fencing device",
                  EC_CONNECTION_LOST: "Connection lost",
                  EC_TIMED_OUT: "Connection timed out",
                  EC_WAITING_ON: "Failed: Timed out waiting to power ON",
                  EC_WAITING_OFF: "Failed: Timed out waiting to power OFF",
                  EC_STATUS: "Failed: Unable to obtain correct plug status or plug is not available",
                  EC_STATUS_HMC: "Failed: Either unable to obtaion correct plug status, partition is not available or incorrect HMC version used"
              }[error_code] + "\n"
    sys.stderr.write(message)
    sys.exit(EC_GENERIC_ERROR)


def usage(avail_opt):
    global all_opt

    print "Usage:"
    print "\t" + os.path.basename(sys.argv[0]) + " [options]"
    print "Options:"

    sorted_list = [(key, all_opt[key]) for key in avail_opt]
    sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"]))

    for key, value in sorted_list:
        if len(value["help"]) != 0:
            print "   " + value["help"]


def metadata(avail_opt, options, docs):
    global all_opt

    sorted_list = [(key, all_opt[key]) for key in avail_opt]
    sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"]))

    print "<?xml version=\"1.0\" ?>"
    print "<resource-agent name=\"" + os.path.basename(sys.argv[0]) + "\" shortdesc=\"" + docs["shortdesc"] + "\" >"
    print "<longdesc>" + docs["longdesc"] + "</longdesc>"
    if docs.has_key("vendorurl"):
        print "<vendor-url>" + docs["vendorurl"] + "</vendor-url>"
    print "<parameters>"
    for option, value in sorted_list:
        if all_opt[option].has_key("shortdesc"):
            print "\t<parameter name=\"" + option + "\" unique=\"1\" required=\"" + all_opt[option]["required"] + "\">"

            default = ""
            if all_opt[option].has_key("default"):
                default = "default=\"" + str(all_opt[option]["default"]) + "\""
            elif options.has_key("-" + all_opt[option]["getopt"][:-1]):
                if options["-" + all_opt[option]["getopt"][:-1]]:
                    try:
                        default = "default=\"" + options["-" + all_opt[option]["getopt"][:-1]] + "\""
                    except TypeError:
                        ## @todo/@note: Currently there is no clean way how to handle lists
                        ## we can create a string from it but we can't set it on command line
                        default = "default=\"" + str(options["-" + all_opt[option]["getopt"][:-1]]) + "\""
            elif options.has_key("-" + all_opt[option]["getopt"]):
                default = "default=\"true\" "

            mixed = all_opt[option]["help"]
            ## split it between option and help text
            res = re.compile("^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed)
            if (None != res):
                mixed = res.group(1)
            mixed = mixed.replace("<", "&lt;").replace(">", "&gt;")
            print "\t\t<getopt mixed=\"" + mixed + "\" />"

            if all_opt[option]["getopt"].count(":") > 0:
                print "\t\t<content type=\"string\" " + default + " />"
            else:
                print "\t\t<content type=\"boolean\" " + default + " />"

            print "\t\t<shortdesc lang=\"en\">" + all_opt[option]["shortdesc"] + "</shortdesc>"
            print "\t</parameter>"
    print "</parameters>"
    print "<actions>"
    if avail_opt.count("io_fencing") == 0:
        print "\t<action name=\"on\" />"
        print "\t<action name=\"off\" />"
        print "\t<action name=\"reboot\" />"
    else:
        print "\t<action name=\"enable\" />"
        print "\t<action name=\"disable\" />"

	print "\t<action name=\"status\" />"
	print "\t<action name=\"list\" />"
	print "\t<action name=\"monitor\" />"
	print "\t<action name=\"metadata\" />"	
	print "</actions>"
	print "</resource-agent>"

def process_input(avail_opt, parameter):
	global all_opt
	global common_opt

	##
	## Add options which are available for every fence agent
	#####
	avail_opt.extend(common_opt)

	##
	## Set standard environment
	#####
	os.putenv("LANG", "C")
	os.putenv("LC_ALL", "C")

	##
	## Prepare list of options for getopt
	#####
	getopt_string = ""
	longopt_list = [ ]
	for k in avail_opt:
		if all_opt.has_key(k):
			getopt_string += all_opt[k]["getopt"]
		else:
			fail_usage("Parse error: unknown option '"+k+"'")

		if all_opt.has_key(k) and all_opt[k].has_key("longopt"):
			if all_opt[k]["getopt"].endswith(":"):
				longopt_list.append(all_opt[k]["longopt"] + "=")
			else:
				longopt_list.append(all_opt[k]["longopt"])

	## Compatibility layer
	if avail_opt.count("module_name") == 1:
		getopt_string += "n:"
		longopt_list.append("plug=")

	try:
		opt, args = getopt.gnu_getopt(parameter.split(' '), getopt_string, longopt_list)
	except getopt.GetoptError, error:
		fail_usage("Parse error: " + error.msg)

    ## Transform longopt to short one which are used in fencing agents
    #####
	old_opt = opt
	opt = { }
 	for o in dict(old_opt).keys():
 		if o.startswith("--"):
 			for x in all_opt.keys():
 				if all_opt[x].has_key("longopt") and "--" + all_opt[x]["longopt"] == o:
 					opt["-" + all_opt[x]["getopt"].rstrip(":")] = dict(old_opt)[o]
 		else:
 			opt[o] = dict(old_opt)[o]

 	## Compatibility Layer
 	#####
 	z = dict(opt)
 	if z.has_key("-T") == 1:
 		z["-o"] = "status"
 	if z.has_key("-n") == 1:
 		z["-m"] = z["-n"]

 	opt = z
 	##
 	#####
	
	##
	## Read options from command line or standard input
	#####
	# if len(sys.argv) > 1:
	# 	try:
	# 		opt, args = getopt.gnu_getopt(sys.argv[1:], getopt_string, longopt_list)
	# 	except getopt.GetoptError, error:
	# 		fail_usage("Parse error: " + error.msg)
    #
	# 	## Transform longopt to short one which are used in fencing agents
	# 	#####
	# 	old_opt = opt
	# 	opt = { }
	# 	for o in dict(old_opt).keys():
	# 		if o.startswith("--"):
	# 			for x in all_opt.keys():
	# 				if all_opt[x].has_key("longopt") and "--" + all_opt[x]["longopt"] == o:
	# 					opt["-" + all_opt[x]["getopt"].rstrip(":")] = dict(old_opt)[o]
	# 		else:
	# 			opt[o] = dict(old_opt)[o]
    #
	# 	## Compatibility Layer
	# 	#####
	# 	z = dict(opt)
	# 	if z.has_key("-T") == 1:
	# 		z["-o"] = "status"
	# 	if z.has_key("-n") == 1:
	# 		z["-m"] = z["-n"]
    #
	# 	opt = z
	# 	##
	# 	#####
	# else:
	# 	opt = { }
	# 	name = ""
	# 	for line in sys.stdin.readlines():
	# 		line = line.strip()
	# 		if ((line.startswith("#")) or (len(line) == 0)):
	# 			continue
    #
	# 		(name, value) = (line + "=").split("=", 1)
	# 		value = value[:-1]
    #
	# 		## Compatibility Layer
	# 		######
	# 		if name == "blade":
	# 			name = "port"
	# 		elif name == "option":
	# 			name = "action"
	# 		elif name == "fm":
	# 			name = "port"
	# 		elif name == "hostname":
	# 			name = "ipaddr"
	# 		elif name == "modulename":
	# 			name = "module_name"
	# 		elif name == "action" and 1 == avail_opt.count("io_fencing"):
	# 			name = "io_fencing"
	# 		elif name == "port" and 1 == avail_opt.count("drac_version"):
	# 			name = "module_name"
    #
	# 		##
	# 		######
	# 		if avail_opt.count(name) == 0:
	# 			sys.stderr.write("Parse error: Ignoring unknown option '"+line+"'\n")
	# 			continue
    #
	# 		if all_opt[name]["getopt"].endswith(":"):
	# 			opt["-"+all_opt[name]["getopt"].rstrip(":")] = value
	# 		elif ((value == "1") or (value.lower() == "yes") or (value.lower() == "on") or (value.lower() == "true")):
	# 			opt["-"+all_opt[name]["getopt"]] = "1"
	return opt

##
## This function checks input and answers if we want to have same answers 
## in each of the fencing agents. It looks for possible errors and run
## password script to set a correct password
######
def check_input(device_opt, opt):
	global all_opt
	global common_opt

	##
	## Add options which are available for every fence agent
	#####
	device_opt.extend([x for x in common_opt if device_opt.count(x) == 0])
	
	options = dict(opt)
	options["device_opt"] = device_opt

	## Set requirements that should be included in metadata
	#####
	if device_opt.count("login") and device_opt.count("no_login") == 0:
		all_opt["login"]["required"] = "1"
	else:
		all_opt["login"]["required"] = "0"

	## In special cases (show help, metadata or version) we don't need to check anything
	#####
	if options.has_key("-h") or options.has_key("-V") or (options.has_key("-o") and options["-o"].lower() == "metadata"):
		return options;

	## Set default values
	#####
	for opt in device_opt:
		if all_opt[opt].has_key("default"):
			getopt = "-" + all_opt[opt]["getopt"].rstrip(":")
			if 0 == options.has_key(getopt):
				options[getopt] = all_opt[opt]["default"]

	options["-o"]=options["-o"].lower()

	if options.has_key("-v"):
		options["log"] = LOG_MODE_VERBOSE
	else:
		options["log"] = LOG_MODE_QUIET

	if 0 == device_opt.count("io_fencing"):
		if 0 == ["on", "off", "reboot", "status", "list", "monitor"].count(options["-o"].lower()):
			fail_usage("Failed: Unrecognised action '" + options["-o"] + "'")
	else:
		if 0 == ["enable", "disable", "status", "list", "monitor"].count(options["-o"].lower()):
			fail_usage("Failed: Unrecognised action '" + options["-o"] + "'")
		
	if (0 == options.has_key("-l")) and device_opt.count("login") and (device_opt.count("no_login") == 0):
		fail_usage("Failed: You have to set login name")

	if 0 == options.has_key("-a") and 0 == options.has_key("-s"):
		fail_usage("Failed: You have to enter fence address")

	if (device_opt.count("no_password") == 0):
		if 0 == device_opt.count("identity_file"):
			if 0 == (options.has_key("-p") or options.has_key("-S")):
				fail_usage("Failed: You have to enter password or password script")
			else: 
				if 0 == (options.has_key("-p") or options.has_key("-S") or options.has_key("-k")):
					fail_usage("Failed: You have to enter password, password script or identity file")

	if 0 == options.has_key("-x") and 1 == options.has_key("-k"):
		fail_usage("Failed: You have to use identity file together with ssh connection (-x)")

	if 1 == options.has_key("-k"):
		if 0 == os.path.isfile(options["-k"]):
			fail_usage("Failed: Identity file " + options["-k"] + " does not exist")

	if (0 == ["list", "monitor"].count(options["-o"].lower())) and (0 == options.has_key("-n") and 0 == options.has_key("-U")) and (device_opt.count("port")):
		fail_usage("Failed: You have to enter plug number")

	if options.has_key("-S"):
		options["-p"] = os.popen(options["-S"]).read().rstrip()

	if options.has_key("-D"):
		try:
			options["debug_fh"] = file (options["-D"], "w")
		except IOError:
			fail_usage("Failed: Unable to create file "+options["-D"])

	if options.has_key("-v") and options.has_key("debug_fh") == 0:
		options["debug_fh"] = sys.stderr

	if options.has_key("-R"):
		options["-P"] = os.popen(options["-R"]).read().rstrip()

	if options.has_key("-u") == False:
		if options.has_key("-x"):
			options["-u"] = 22
		elif options.has_key("-z"):
			options["-u"] = 443
		elif device_opt.count("web"):
			options["-u"] = 80
		else:
			options["-u"] = 23

	return options
	
def wait_power_status(tn, options, get_power_fn):
	for dummy in xrange(int(options["-g"])):
		if get_power_fn(tn, options) != options["-o"]:
			time.sleep(1)
		else:
			return 1
	return 0

def show_docs(options, docs = None):
	device_opt = options["device_opt"]

	if docs == None:
		docs = { }
		docs["shortdesc"] = "Fence agent"
		docs["longdesc"] = ""
	
	## Process special options (and exit)
	#####
	if options.has_key("-h"): 
		usage(device_opt)
		sys.exit(0)

	if options.has_key("-o") and options["-o"].lower() == "metadata":
		metadata(device_opt, options, docs)
		sys.exit(0)

	if options.has_key("-V"):
		print __main__.RELEASE_VERSION, __main__.BUILD_DATE
		print __main__.REDHAT_COPYRIGHT
		sys.exit(0)

def fence_action(tn, options, set_power_fn, get_power_fn, get_outlet_list = None):
	result = 0
	
	## Process options that manipulate fencing device
	#####
	if (options["-o"] == "list") and (0 == options["device_opt"].count("port")) and (0 == options["device_opt"].count("partition") and 0 == options["device_opt"].count("uuid") and 0 == options["device_opt"].count("module_name")):
		print "N/A"
		return
	elif (options["-o"] == "list" and get_outlet_list == None):
		## @todo: exception?
		## This is just temporal solution, we will remove default value
		## None as soon as all existing agent will support this operation 
		print "NOTICE: List option is not working on this device yet"
		return
	elif (options["-o"] == "list") or ((options["-o"] == "monitor") and 1 == options["device_opt"].count("port")):
		outlets = get_outlet_list(tn, options)
		## keys can be numbers (port numbers) or strings (names of VM)
		for o in outlets.keys():
			(alias, status) = outlets[o]
			if options["-o"] != "monitor":
				print o + options["-C"] + alias	
		return

	if options["-o"] in ["off", "reboot"]:
		time.sleep(int(options["-f"]))
	status = get_power_fn(tn, options)

	if status != "on" and status != "off":  
		fail(EC_STATUS)

	
	if options["-o"] == "enable":
		options["-o"] = "on"
	if options["-o"] == "disable":
		options["-o"] = "off"

	if options["-o"] == "on":
		if status == "on":
			print "Success: Already ON"
		else:
			power_on = False
			for i in range(1,1 + int(options["-F"])):
				set_power_fn(tn, options)
				time.sleep(int(options["-G"]))
				if wait_power_status(tn, options, get_power_fn):
					power_on = True
					break

			if power_on:
				print "Success: Powered ON"
			else:
				fail(EC_WAITING_ON)
	elif options["-o"] == "off":
		if status == "off":
			print "Success: Already OFF"
		else:
			set_power_fn(tn, options)
			time.sleep(int(options["-G"]))
			if wait_power_status(tn, options, get_power_fn):
				print "Success: Powered OFF"
			else:
				fail(EC_WAITING_OFF)
	elif options["-o"] == "reboot":
		if status != "off":
			options["-o"] = "off"
			set_power_fn(tn, options)
			time.sleep(int(options["-G"]))
			if wait_power_status(tn, options, get_power_fn) == 0:
				fail(EC_WAITING_OFF)
		options["-o"] = "on"

		power_on = False
		for i in range(1,1 + int(options["-F"])):
			set_power_fn(tn, options)
			time.sleep(int(options["-G"]))
			if wait_power_status(tn, options, get_power_fn) == 1:
				power_on = True
				break

		if power_on == False:
			# this should not fail as not was fenced succesfully
			sys.stderr.write('Timed out waiting to power ON\n')

		print "Success: Rebooted"
	elif options["-o"] == "status":
		print "Status: " + status.upper()
		if status.upper() == "OFF":
			result = 2
	elif options["-o"] == "monitor":
		1
	
	return result

def fence_login(options):
	force_ipvx=""

	if (options.has_key("-6")):
		force_ipvx="-6 "

	if (options.has_key("-4")):
		force_ipvx="-4 "

	if (options["device_opt"].count("login_eol_lf")):
		login_eol = "\n"
	else:
		login_eol = "\r\n"

	try:
		re_login = re.compile("(login\s*: )|(Login Name:  )|(username: )|(User Name :)", re.IGNORECASE)
		re_pass  = re.compile("password", re.IGNORECASE)

		if options.has_key("-z"):
			command = '%s %s %s %s' % (SSL_PATH, force_ipvx, options["-a"], options["-u"])
			try:
				conn = fspawn(command)
			except pexpect.ExceptionPexpect, ex:
			 	## SSL telnet is part of the fencing package
			 	sys.stderr.write(str(ex) + "\n")
			 	sys.exit(EC_GENERIC_ERROR)
		elif options.has_key("-x") and 0 == options.has_key("-k"):
			command = '%s %s %s@%s -p %s' % (SSH_PATH, force_ipvx, options["-l"], options["-a"], options["-u"])
			if options.has_key("ssh_options"):
				command += ' ' + options["ssh_options"]
			try:
				conn = fspawn(command)
			except pexpect.ExceptionPexpect, ex:
				sys.stderr.write(str(ex) + "\n")
				sys.stderr.write("Due to limitations, binary dependencies on fence agents "
				"are not in the spec file and must be installed separately." + "\n")
				sys.exit(EC_GENERIC_ERROR)
				
			if options.has_key("telnet_over_ssh"):
				#This is for stupid ssh servers (like ALOM) which behave more like telnet (ignore name and display login prompt)
				result = conn.log_expect(options, [ re_login, "Are you sure you want to continue connecting (yes/no)?" ], int(options["-y"]))
				if result == 1:
					conn.sendline("yes") # Host identity confirm
					conn.log_expect(options, re_login, int(options["-y"]))

				conn.sendline(options["-l"])
				conn.log_expect(options, re_pass, int(options["-y"]))
			else:
				result = conn.log_expect(options, [ "ssword:", "Are you sure you want to continue connecting (yes/no)?" ], int(options["-y"]))
				if result == 1:
					conn.sendline("yes")
					conn.log_expect(options, "ssword:", int(options["-y"]))

			conn.sendline(options["-p"])
			conn.log_expect(options, options["-c"], int(options["-y"]))
		elif options.has_key("-x") and 1 == options.has_key("-k"):
			command = '%s %s %s@%s -i %s -p %s' % (SSH_PATH, force_ipvx, options["-l"], options["-a"], options["-k"], options["-u"])
			if options.has_key("ssh_options"):
				command += ' ' + options["ssh_options"]
			try:
				conn = fspawn(command)
			except pexpect.ExceptionPexpect, ex:
				sys.stderr.write(str(ex) + "\n")
				sys.stderr.write("Due to limitations, binary dependencies on fence agents "
				"are not in the spec file and must be installed separately." + "\n")
				sys.exit(EC_GENERIC_ERROR)

			result = conn.log_expect(options, [ options["-c"], "Are you sure you want to continue connecting (yes/no)?", "Enter passphrase for key '"+options["-k"]+"':" ], int(options["-y"]))
			if result == 1:
				conn.sendline("yes")
				conn.log_expect(options, [ options["-c"], "Enter passphrase for key '"+options["-k"]+"':"] , int(options["-y"]))
			if result != 0:
				if options.has_key("-p"):
					conn.sendline(options["-p"])
					conn.log_expect(options, options["-c"], int(options["-y"]))
				else:
					fail_usage("Failed: You have to enter passphrase (-p) for identity file")
		else:
			try:
				conn = fspawn(TELNET_PATH)
				conn.send("set binary\n")
				conn.send("open %s -%s\n"%(options["-a"], options["-u"]))
			except pexpect.ExceptionPexpect, ex:
				sys.stderr.write(str(ex) + "\n")
				sys.stderr.write("Due to limitations, binary dependencies on fence agents "
				"are not in the spec file and must be installed separately." + "\n")
				sys.exit(EC_GENERIC_ERROR)

			conn.log_expect(options, re_login, int(options["-y"]))
			conn.send(options["-l"] + login_eol)
			conn.log_expect(options, re_pass, int(options["-Y"]))
			conn.send(options["-p"] + login_eol)
			conn.log_expect(options, options["-c"], int(options["-Y"]))
	except pexpect.EOF:
		#fail(EC_LOGIN_DENIED)
		raise pexpect.EOF()
	except pexpect.TIMEOUT:
		raise pexpect.TIMEOUT()
		#fail(EC_LOGIN_DENIED)
	return conn
